/**
 * call 和 apply 都是为了解决改变 this 的指向。作用都是相同的，只是参数的方式不同。
 * 除了第一个参数外， call 可以接收一个参数列表， apply 只接受一个数组。
 */

let a = {
  value: 1
};

function getValue(name, age){
  console.log(name);
  console.log(age);
  console.log(this.value);
}

// getValue.call(a, 'jck', '24');
// getValue.apply(a, ['jsck', '20']);


/**
* 模拟实现 call
* 可以从以下几点来考虑如何实现
* 1.不传入第一个参数，那么默认为 window
* 2.改变了 this 指向，让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数，然后在执行完以后删除
* 
* 解析：传入的对象，在传入的对象中，创建一个属性，并执行函数，后在删除该对象
*/
Function.prototype.myCall = function(context = window, ...args) {
  // 防止直接调用Function.prototype.myCall()
  if(this === Function.prototype){
    return undefined;
  }
  context = context || window;
  const fn = Symbol();
  context[fn] = this;
  const result = context[fn](...args);
  delete context[fn];
  return result;
}

// getValue.myCall(a, 'kk', '28');


/**
 * 模拟实现 apply
 * apply 实现类似call，参数是数组
 *  
 */

Function.prototype.myApply = function(context = window, args) {
  // 防止直接调用Function.prototype.myCall()
  if(this === Function.prototype) {
    return undefined;
  }
  context = context || window;
  const fn = Symbol();
  context[fn] = this;
  let result;
  if(Array.isArray(args)){
    result = context[fn](...args);
  }else {
    result = context[fn]();
  }
  delete context[fn];
  return result;
}

getValue.myApply(a, ['kim', '18']);
